/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.orogeny.filing.findFileFilters;

import com.softwarecraftsmen.orogeny.filing.AbsoluteDirectory;
import com.softwarecraftsmen.orogeny.filing.FileName;
import com.softwarecraftsmen.orogeny.filing.RelativeDirectory;
import static com.softwarecraftsmen.orogeny.filing.findFileFilters.FileHasExtensionFindFilesFilter.fileHasExtension;
import static com.softwarecraftsmen.orogeny.filing.findSubdirectoriesFilters.AbstractFindSubdirectoriesFilter.HiddenSubdirectoryExcluding;
import com.softwarecraftsmen.orogeny.filing.findSubdirectoriesFilters.FindSubdirectoriesFilter;
import org.jetbrains.annotations.NotNull;

public abstract class AbstractFindFilesFilter implements FindFilesFilter
{
	@NotNull
	public static final FindFilesFilter AllFiles = new AbstractFindFilesFilter()
	{
		public boolean include(final @NotNull AbsoluteDirectory root, final @NotNull RelativeDirectory relativeDirectory, final @NotNull FileName fileName)
		{
			return true;
		}

		@NotNull
		public String toString()
		{
			return "AllFiles";
		}
	};

	@NotNull
	public static final FindFilesFilter HiddenFileExcluding = new AbstractFindFilesFilter()
	{
		public boolean include(final @NotNull AbsoluteDirectory root, final @NotNull RelativeDirectory relativeDirectory, final @NotNull FileName fileName)
		{
			return !relativeDirectory.makeAbsolute(root).existsAndIsHidden(fileName);
		}

		@NotNull
		public String toString()
		{
			return "HiddenFileExcluding";
		}
	};

	@NotNull
	public static final FindFilesFilter AllFilesExcludingHiddenFilesAndHiddenFolders = AllFiles.excludeHiddenFilesAndHiddenFolders();

	@NotNull
	public static final FindFilesFilter NoFiles = AllFiles.not();

	@NotNull
	public static final FindFilesFilter HasClassFileExtension = fileHasExtension("class");

	@NotNull
	public static final FindFilesFilter HasJavaFileExtension = fileHasExtension("java");

	@NotNull
	public static final FindFilesFilter HasZipFileExtension = fileHasExtension("zip");

	@NotNull
	public static final FindFilesFilter HasJarFileExtension = fileHasExtension("jar");

	@NotNull
	public static final FindFilesFilter HasIntelliJFileExtension = fileHasExtension("iml").or(fileHasExtension("iws")).or(fileHasExtension("ipr"));

	@NotNull
	public static final FindFilesFilter JavaClass = HasClassFileExtension.excludeHiddenFilesAndHiddenFolders();

	@NotNull
	public static final FindFilesFilter JavaSource = HasJavaFileExtension.excludeHiddenFilesAndHiddenFolders();

	@NotNull
	public static final FindFilesFilter Zip = HasZipFileExtension.excludeHiddenFilesAndHiddenFolders();

	@NotNull
	public static final FindFilesFilter Jar = HasJarFileExtension.excludeHiddenFilesAndHiddenFolders();

	@NotNull
	public static final FindFilesFilter ZipOrJar = Zip.or(Jar);

	@NotNull
	public static final FindFilesFilter AllFilesExcludingHiddenFilesAndHiddenFoldersAndJavaSource = AllFilesExcludingHiddenFilesAndHiddenFolders.exclude(HasJavaFileExtension);

	@NotNull
	public static FindFilesFilter isInRoot(final @NotNull AbsoluteDirectory ourRoot)
	{
		return new AbstractFindFilesFilter()
		{
			public boolean include(final @NotNull AbsoluteDirectory root, final @NotNull RelativeDirectory relativeDirectory, final @NotNull FileName fileName)
			{
				if (ourRoot.isRoot())
				{
					return true;
				}
				AbsoluteDirectory potentialRoot = root;
				while (!potentialRoot.isRoot())
				{
					if (potentialRoot.equals(ourRoot))
					{
						return true;
					}
					potentialRoot = potentialRoot.parent();
				}
				return false;
			}
		};
	}

	@NotNull
	public static FindFilesFilter adaptFindSubdirectoriesFilter(final @NotNull FindSubdirectoriesFilter findSubdirectoriesFilter)
	{
		return new FindSubdirectoriesFilterAdapterFindFilesFilter(findSubdirectoriesFilter);
	}

	@NotNull
	public final FindFilesFilter exclude(final @NotNull FindFilesFilter exclude)
	{
		return this.and(exclude.not());
	}

	@NotNull
	public final FindFilesFilter excludeHiddenFilesAndHiddenFolders()
	{
		return and(HiddenFileExcluding).and(adaptFindSubdirectoriesFilter(HiddenSubdirectoryExcluding));
	}

	public final boolean exclude(final @NotNull AbsoluteDirectory root, final @NotNull RelativeDirectory relativeDirectory, final @NotNull FileName fileName)
	{
		return !include(root, relativeDirectory, fileName);
	}

	@NotNull
	public final FindFilesFilter and(final @NotNull FindFilesFilter that)
	{
		return new AbstractFindFilesFilter()
		{
			public boolean include(final @NotNull AbsoluteDirectory root, final @NotNull RelativeDirectory relativeDirectory, final @NotNull FileName fileName)
			{
				return AbstractFindFilesFilter.this.include(root, relativeDirectory, fileName) && that.include(root, relativeDirectory, fileName);
			}

			@NotNull
			public String toString()
			{
				return AbstractFindFilesFilter.this.toString() + " and ( " + that.toString() + " )";
			}
		};
	}

	@NotNull
	public final FindFilesFilter or(final @NotNull FindFilesFilter that)
	{
		return new AbstractFindFilesFilter()
		{
			public boolean include(final @NotNull AbsoluteDirectory root, final @NotNull RelativeDirectory relativeDirectory, final @NotNull FileName fileName)
			{
				return AbstractFindFilesFilter.this.include(root, relativeDirectory, fileName) || that.include(root, relativeDirectory, fileName);
			}

			@NotNull
			public String toString()
			{
				return AbstractFindFilesFilter.this.toString() + " or ( " + that.toString() + " )";
			}
		};
	}

	@NotNull
	public final FindFilesFilter not()
	{
		return new AbstractFindFilesFilter()
		{
			public boolean include(final @NotNull AbsoluteDirectory root, final @NotNull RelativeDirectory relativeDirectory, final @NotNull FileName fileName)
			{
				return !AbstractFindFilesFilter.this.include(root, relativeDirectory, fileName);
			}

			@NotNull
			public String toString()
			{
				return "not( " + AbstractFindFilesFilter.this.toString() + " )";
			}
		};
	}

	@NotNull
	public String toString()
	{
		return getClass().getSimpleName();
	}
}
